﻿using System;
using System.Collections.Generic;
using Behavioral.Private;

namespace Behavioral
{
    public static class ContextExtensionMethods
    {
        [ThreadStatic] 
        private static IDictionary<Type, object> anonymousContextDictionary;
        [ThreadStatic]
        private static IDictionary<string, object> namedObjectContextDictionary;

        [ThreadStatic] 
        private static dynamic dynamicContext;

        private static IDictionary<Type, object> AnonymousContextDictionary
        {
            get
            {
                if(anonymousContextDictionary == null)
                {
                    anonymousContextDictionary = new Dictionary<Type, object>();
                }
                return anonymousContextDictionary;
            }
        }
        
        private static IDictionary<string, object> NamedObjectContextDictionary
        {
            get
            {
                if (namedObjectContextDictionary == null)
                {
                    namedObjectContextDictionary = new Dictionary<string, object>();
                }
                return namedObjectContextDictionary;
            }
        }
        
        private static DynamicContext DynamicContext
        {
            get
            {
                if(dynamicContext == null)
                {
                    dynamicContext = new DynamicContext();
                }
                return dynamicContext;
            }
        }
        
        public static IContextSetter SetContext<TContext>(this IContextSetter contextSetter, TContext contextValue)
        {
            AnonymousContextDictionary[typeof(TContext)] = contextValue;
            return contextSetter;
        }

        public static IContextSetter SetContext<TContext>(this IContextSetter contextSetter, string name, TContext contextValue)
        {
            NamedObjectContextDictionary[name] = contextValue;
            return contextSetter;
        }

        public static IContextSetter SetContext(this IContextSetter contextSetter, Action<dynamic> contextSetterAction)
        {
            contextSetterAction.Invoke(DynamicContext);
            return contextSetter;
        }

        public static TContext GetContext<TContext>(this IContextGetter contextGetter)
        {
            TContext contextValue = default(TContext);
            if (AnonymousContextDictionary.ContainsKey(typeof(TContext)))
            {
                contextValue = (TContext)AnonymousContextDictionary[typeof(TContext)];
            }
            return contextValue;
        }

        public static TContext GetContext<TContext>(this IContextGetter contextGetter, string name)
        {
            TContext contextValue = default(TContext);
            if (NamedObjectContextDictionary.ContainsKey(name))
            {
                contextValue = (TContext) NamedObjectContextDictionary[name];
            }
            return contextValue;
        }

        public static dynamic Context(this IContextGetter contextGetter)
        {
            return DynamicContext;
        }

        internal static object GetContext(this IContextGetterInternal contextGetter, Type contextType)
        {
            object contextValue = null;
            if (AnonymousContextDictionary.ContainsKey(contextType))
            {
                contextValue = AnonymousContextDictionary[contextType];
            }
            return contextValue;
        }

        internal static IContextSetterInternal SetContext(this IContextSetterInternal contextSetter, Type contextType, object contextValue)
        {
            AnonymousContextDictionary[contextType] = contextValue;
            return contextSetter;
        }

        internal static object GetContext(this IDynamicContextGetter contextGetter, string name)
        {
            object contextValue = null;
            if (NamedObjectContextDictionary.ContainsKey(name))
            {
                contextValue = NamedObjectContextDictionary[name];
            }
            return contextValue;
        }

        internal static IDynamicContextSetter SetContext(this IDynamicContextSetter contextSetter, string name, object contextValue)
        {
            NamedObjectContextDictionary[name] = contextValue;
            return contextSetter;
        }

        internal static void ClearContext(this IContextClearerInternal contextClearer)
        {
            AnonymousContextDictionary.Clear();
            NamedObjectContextDictionary.Clear();
        }
    }
}
